#ifndef EASYASIO_NET_TCP_CONNECTION_HPP
#define EASYASIO_NET_TCP_CONNECTION_HPP

#include <array>
#include <memory>

#include <boost/any.hpp>
#include <asio.hpp>

#include "callbacks.hpp"
#include "../base/utils.hpp"

namespace easyasio {
using namespace base;
namespace net {

class TcpConnection : public std::enable_shared_from_this<TcpConnection>, private noncopyable 
{
public:
	enum State {Disconnected, Connecting, Connected, Disconnecting, forceclosing};

	explicit TcpConnection(asio::io_service& io_service);
    ~TcpConnection();
	void send(const std::string& msg);
    void shutdown();
	void start();
    void forceClose();
    bool connected() { return state() == Connected; }
	asio::ip::tcp::socket& socket();
    State state() const { return state_; }
    void setTcpNoDelay(bool opt) { socket_.set_option(asio::ip::tcp::no_delay(opt)); }

	void setConnectionCallback(const ConnectionCallback& cb) { connectionCallback_ = cb; }
	void setMessageCallback(const MessageCallback& cb) { messageCallback_ = cb; }
	void setCloseCallback(const CloseCallback& cb) { closeCallback_ = cb; }
	void setWriteCompleteCallback(const WriteCompleteCallback& cb) { writeCompleteCallback_ = cb; }
	void setHighWaterMarkCallback(const HighWaterMarkCallback& cb, size_t highWaterMark) 
    { highWaterMarkCallback_ = cb; highWaterMark_ = highWaterMark; }

    void setContext(const mnb::any& context)
    { context_ = context; }

    const mnb::any& getContext() const
    { return context_; }

private:
	void handleReadableInLoop(const asio::error_code& e, std::size_t bytes_transferred);
	void handleWritableInLoop(const asio::error_code& e);
    void shutdownInLoop();
	void sendInLoop(const std::string& msg);
	void socketTrySendInloop();
    void forceCloseInloop();
    void setState(State s) { state_ = s; }
    void handleCloseInLoop();
    bool isWriting() { return sending_buffer_.readableBytes() != 0; }
    bool permitSendInLoop() { return state() == Connected || state() == Disconnecting; }

private:
	asio::ip::tcp::socket               socket_;

	//buffers
	static const int                    RECV_CACHED_SIZE = 8192;
    Buffer                              recv_buffer_;

	Buffer                 	            sending_buffer_;
	Buffer                 	            pending_send_buffer_;

	//callbacks
	ConnectionCallback                  connectionCallback_;
	MessageCallback                     messageCallback_;
	HighWaterMarkCallback               highWaterMarkCallback_;
	size_t                              highWaterMark_;
	WriteCompleteCallback               writeCompleteCallback_;
    CloseCallback                       closeCallback_;

	asio::io_service&                   loop_;
	State                               state_;
    mnb::any                            context_;
};

} // namespace net 
} // namespace easyasio 

#endif // EASYASIO_NET_TCP_CONNECTION_HPP
